// Copyright (C) 2009, Bronislaw (Bronek) Kozicki
//
// Use, modification, and distribution is subject to MIT License
// For details see http://opensource.org/licenses/mit-license.php
//
// NO WARRANTY OF ANY KIND, EITHER IMPLIED OR EXPRESS, ETC. ETC.
//

#include <stdexcept>
#include <windows.h>
#include <intrin.h>

namespace util
{
	template <typename T>
	class single_ptr
	{
		enum {Undef, Trans1, Empty, Trans2, Alive}; 
		static __declspec(align(8)) volatile long ref_counter_;
		static __declspec(align(8)) volatile long ref_state_;
		static const T * ref_impl_;

		typedef const T* (*acquire_t)(const void*);
		typedef void (*release_t)(const void*, const T*);
		static acquire_t acquire_;
		static release_t release_;
		static const void* parameter_;

		template <typename Def>
		static const T* acquire(const void* p)
		{
			Def* dp = (Def *)p;
			return dp->acquire();
		}

		template <typename Def>
		static void release(const void* p, const T* tp)
		{
			const Def* dp = (Def *)p;
			dp->release(tp);
		}

		struct def_default
		{
			const T* acquire() const {return new T();}
			void release(const T* p) const {delete p;}
		};

		template <typename Arg1>
		struct def_arg1
		{
			const Arg1 arg1;
			const T* acquire() const {return new T(arg1);}
			void release(const T* p) const {delete p;}
		};

		template <typename Arg1, typename Arg2>
		struct def_arg2
		{
			const Arg1 arg1;
			const Arg2 arg2;
			const T* acquire() const {return new T(arg1, arg2);}
			void release(const T* p) const {delete p;}
		};

		template <typename Arg1, typename Arg2, typename Arg3>
		struct def_arg3
		{
			const Arg1 arg1;
			const Arg2 arg2;
			const Arg3 arg3;
			const T* acquire() const {return new T(arg1, arg2, arg3);}
			void release(const T* p) const {delete p;}
		};

		template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
		struct def_arg4
		{
			const Arg1 arg1;
			const Arg2 arg2;
			const Arg3 arg3;
			const Arg4 arg4;
			const T* acquire() const {return new T(arg1, arg2, arg3, arg4);}
			void release(const T* p) const {delete p;}
		};

		template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
		struct def_arg5
		{
			const Arg1 arg1;
			const Arg2 arg2;
			const Arg3 arg3;
			const Arg4 arg4;
			const Arg5 arg5;
			const T* acquire() const {return new T(arg1, arg2, arg3, arg4, arg5);}
			void release(const T* p) const {delete p;}
		};

	public:
		struct error : std::logic_error
		{
			enum {Undef, Redef};
			explicit error(int c) 
				: logic_error(c == Undef ? "init() has not been called" : "init() called more than once")
				, code(c)
			{}
			const int code;
		};

		single_ptr()
		{
			for(;;)
			{
				if (_InterlockedIncrement(&ref_counter_) == 1)
				{
					for (;;)
					{
						const int e = _InterlockedCompareExchange(&ref_state_, Trans2, Empty);
						if (e == Empty)
							break;
						else if (e == Undef)
							throw error(error::Undef);

						::Sleep(1);
					}

					const T* impl = NULL;
					try
					{
						impl = acquire_(parameter_);
					}
					catch(...)
					{
						_InterlockedExchange(&ref_state_, Empty);
						_InterlockedDecrement(&ref_counter_);
						throw;
					}

					ref_impl_ = impl;
					_InterlockedExchange(&ref_state_, Alive);
					break;
				}
				else
				{
					if (_InterlockedCompareExchange(&ref_state_, Alive, Alive) == Alive)
						break;

					_InterlockedDecrement(&ref_counter_);
					::Sleep(1);
				}
			}
		}

		single_ptr(const single_ptr& src)
		{
			_InterlockedIncrement(&ref_counter_);
		}

		// No need to declare = , default generated is just fine
		// single_ptr& operator=(const single_ptr& src);

		const T * operator-> () const
		{
			return ref_impl_;
		}

		const T& get() const
		{
			return *ref_impl_;
		}

		const T& operator* () const
		{
			return get();
		}

		static void init()
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			static const def_default d;
			acquire_ = &acquire<def_default>;
			release_ = &release<def_default>;
			parameter_ = (const void*) &d;

			_InterlockedExchange(&ref_state_, Empty);
		}

		template <typename Arg1>
		static void init(const Arg1& a1)
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			static const def_arg1<Arg1> d = {a1};
			acquire_ = &acquire<def_arg1<Arg1> >;
			release_ = &release<def_arg1<Arg1> >;
			parameter_ = (const void*) &d;

			_InterlockedExchange(&ref_state_, Empty);
		}

		template <typename Arg1, typename Arg2>
		static void init(const Arg1& a1, const Arg2& a2)
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			static const def_arg2<Arg1, Arg2> d = {a1, a2};
			acquire_ = &acquire<def_arg2<Arg1, Arg2> >;
			release_ = &release<def_arg2<Arg1, Arg2> >;
			parameter_ = (const void*) &d;

			_InterlockedExchange(&ref_state_, Empty);
		}

		template <typename Arg1, typename Arg2, typename Arg3>
		static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3)
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			static const def_arg3<Arg1, Arg2, Arg3> d = {a1, a2, a3};
			acquire_ = &acquire<def_arg3<Arg1, Arg2, Arg3> >;
			release_ = &release<def_arg3<Arg1, Arg2, Arg3> >;
			parameter_ = (const void*) &d;

			_InterlockedExchange(&ref_state_, Empty);
		}

		template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
		static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3, const Arg4& a4)
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			static const def_arg4<Arg1, Arg2, Arg3, Arg4> d = {a1, a2, a3, a4};
			acquire_ = &acquire<def_arg4<Arg1, Arg2, Arg3, Arg4> >;
			release_ = &release<def_arg4<Arg1, Arg2, Arg3, Arg4> >;
			parameter_ = (const void*) &d;

			_InterlockedExchange(&ref_state_, Empty);
		}

		template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
		static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3, const Arg4& a4, const Arg5& a5)
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			static const def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> d = {a1, a2, a3, a4, a5};
			acquire_ = &acquire<def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> >;
			release_ = &release<def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> >;
			parameter_ = (const void*) &d;

			_InterlockedExchange(&ref_state_, Empty);
		}

		template <typename Def>
		static void init(const Def* pd)
		{
			if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef)
				throw error(error::Redef);

			acquire_ = &acquire<Def>;
			release_ = &release<Def>;
			parameter_ = (const void*) pd;

			_InterlockedExchange(&ref_state_, Empty);
		}

		~single_ptr()
		{
			if (_InterlockedDecrement(&ref_counter_) == 0)
			{
				while (_InterlockedCompareExchange(&ref_state_, Trans2, Alive) != Alive)
					::Sleep(1);

				const T* impl = ref_impl_;
				ref_impl_ = NULL;
				_InterlockedExchange(&ref_state_, Empty);

				release_(parameter_, impl);
			}
		}
	};

	template <typename T>
	__declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0;

	template <typename T>
	__declspec(align(8)) volatile long single_ptr<T>::ref_state_ = single_ptr<T>::Undef;

	template <typename T>
	const T * single_ptr<T>::ref_impl_ = NULL;

	template <typename T>
	typename single_ptr<T>::acquire_t single_ptr<T>::acquire_ = NULL;

	template <typename T>
	typename single_ptr<T>::release_t single_ptr<T>::release_ = NULL;

	template <typename T>
	const void* single_ptr<T>::parameter_ = NULL;
}

